﻿#region License
/**	
The MIT License (MIT)

Copyright (c) 2013 Philipp Schillinger, Michael Alekseew, Andreas Braun, Steeven Zeiß

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. 
 **/
#endregion

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Threading;
using System.Text;
using System.Windows.Forms;

namespace IndoorLocalization.UserInterface {
	/// <summary>
	/// Realizes a window that shows the properties of a cam 
	/// and allows changing of its parameters.
	/// </summary>
	partial class CamPropertiesWindow : Form {

		/// <summary>
		/// The cam that corresponds to this window.
		/// </summary>
		private SensorCam cam;

		/// <summary>
		/// Opens a new window that allows configuring of the given cams parameter.
		/// </summary>
		/// <param name="ownCam">Cam for which the properties window should be created.</param>
		public CamPropertiesWindow(SensorCam ownCam) {
			InitializeComponent();
			cam = ownCam;
		}

		/// <summary>
		/// Loads the window by determining the current parameter values.
		/// </summary>
		/// <param name="sender">Not used.</param>
		/// <param name="e">Not used.</param>
		private void CamPropertiesWindow_Load(object sender, EventArgs e) {
			// initialize form elements
			Text = "Properties of " + cam.Name;
			guidBox.Text = cam.GUID.ToString();
			nameBox.Text = cam.Name;
			gainAuto.Checked = cam.Device.AutoGain;
			exposureAuto.Checked = cam.Device.AutoExposure;
			balanceAuto.Checked = cam.Device.AutoWhiteBalance;
			gainAdjust.Enabled = !cam.Device.AutoGain;
			exposureAdjust.Enabled = !cam.Device.AutoExposure;
			redAdjust.Enabled = !cam.Device.AutoWhiteBalance;
			greenAdjust.Enabled = !cam.Device.AutoWhiteBalance;
			blueAdjust.Enabled = !cam.Device.AutoWhiteBalance;

			camX.Maximum = FloorPlan.MaxX;
			camY.Maximum = FloorPlan.MaxY;
			camX.Value = cam.X;
			camY.Value = cam.Y;
			camOrientation.Value = (int)cam.Orientation;

			camPitch.Value = (decimal)cam.Pitch;

			// create merger selection
			if (cam.Merger.IsLocal()) mergerSelect.Text = cam.Merger.Name;
			else mergerSelect.Text = cam.Merger.Name + "@" + ((RemoteMerger)cam.Merger).NetworkID;

			foreach (SensorMerger merger in Program.Mergers)
				mergerSelect.Items.Add(merger.Name);
			foreach (RemoteMerger merger in Program.RemoteMergers)
				mergerSelect.Items.Add(merger.Name + "@" + merger.NetworkID);

			// remove the cam from the coverage map while this window is open
			Program.UpdateCoverageMap(cam, false);

			// add closing routine
			cam.Disposed += delegate {
				Close();
			};
		}

		// Adjust Parameters
		#region Parameters
		private void gainAuto_CheckedChanged(object sender, EventArgs e) {
			cam.Device.AutoGain = !cam.Device.AutoGain;
			gainAuto.Checked = cam.Device.AutoGain;
			gainAdjust.Enabled = !cam.Device.AutoGain;
			
		}
		private void exposureAuto_CheckedChanged(object sender, EventArgs e) {
			cam.Device.AutoExposure = !cam.Device.AutoExposure;
			exposureAuto.Checked = cam.Device.AutoExposure;
			exposureAdjust.Enabled = !cam.Device.AutoExposure;
		}
		private void balanceAuto_CheckedChanged(object sender, EventArgs e) {
			cam.Device.AutoWhiteBalance = !cam.Device.AutoWhiteBalance;
			balanceAuto.Checked = cam.Device.AutoWhiteBalance;
			redAdjust.Enabled = !cam.Device.AutoWhiteBalance;
			greenAdjust.Enabled = !cam.Device.AutoWhiteBalance;
			blueAdjust.Enabled = !cam.Device.AutoWhiteBalance;
		}

		private void gainAdjust_Scroll(object sender, EventArgs e) {
			cam.Device.Gain = (int)(gainAdjust.Value / 100.0 * 79);
		}
		private void exposureAdjust_Scroll(object sender, EventArgs e) {
			cam.Device.Exposure = (int)(exposureAdjust.Value / 100.0 * 511);
		}
		private void redAdjust_Scroll(object sender, EventArgs e) {
			cam.Device.WhiteBalanceRed = (int)(redAdjust.Value / 100.0 * 255);
		}
		private void greenAdjust_Scroll(object sender, EventArgs e) {
			cam.Device.WhiteBalanceGreen = (int)(greenAdjust.Value / 100.0 * 255);
		}
		private void blueAdjust_Scroll(object sender, EventArgs e) {
			cam.Device.WhiteBalanceBlue = (int)(blueAdjust.Value / 100.0 * 255);
		}
		#endregion

		// Adjust Position
		#region Position
		private void camPosU_Click(object sender, EventArgs e) {
			camY.Value += camTransStep.Value;
			if (camY.Value - camTransStep.Value >= 0) {
				camPosDR.Enabled = (camX.Value + camTransStep.Value) <= FloorPlan.MaxX;
				camPosD.Enabled = true;
				camPosDL.Enabled = (camX.Value - camTransStep.Value) >= 0;
			}
			if (camY.Value + camTransStep.Value > FloorPlan.MaxY) {
				camPosUR.Enabled = false;
				camPosU.Enabled = false;
				camPosUL.Enabled = false;
			}
		}
		private void camPosUR_Click(object sender, EventArgs e) {
			camPosU_Click(sender, e);
			camPosR_Click(sender, e);
		}
		private void camPosR_Click(object sender, EventArgs e) {
			camX.Value += camTransStep.Value;
			if (camX.Value - camTransStep.Value >= 0) {
				camPosUL.Enabled = (camY.Value + camTransStep.Value) <= FloorPlan.MaxY;
				camPosL.Enabled = true;
				camPosDL.Enabled = (camY.Value - camTransStep.Value) >= 0;
			}
			if (camX.Value + camTransStep.Value > FloorPlan.MaxX) {
				camPosUR.Enabled = false;
				camPosR.Enabled = false;
				camPosDR.Enabled = false;
			}
		}
		private void camPosDR_Click(object sender, EventArgs e) {
			camPosD_Click(sender, e);
			camPosR_Click(sender, e);
		}
		private void camPosD_Click(object sender, EventArgs e) {
			camY.Value -= camTransStep.Value;
			if (camY.Value - camTransStep.Value < 0) {
				camPosDR.Enabled = false;
				camPosD.Enabled = false;
				camPosDL.Enabled = false;
			}
			if (camY.Value + camTransStep.Value <= FloorPlan.MaxY) {
				camPosUR.Enabled = (camX.Value + camTransStep.Value) <= FloorPlan.MaxX;
				camPosU.Enabled = true;
				camPosUL.Enabled = (camX.Value - camTransStep.Value) >= 0;
			}
		}
		private void camPosDL_Click(object sender, EventArgs e) {
			camPosD_Click(sender, e);
			camPosL_Click(sender, e);
		}
		private void camPosL_Click(object sender, EventArgs e) {
			camX.Value -= camTransStep.Value;
			if (camX.Value - camTransStep.Value < 0) {
				camPosUL.Enabled = false;
				camPosL.Enabled = false;
				camPosDL.Enabled = false;
			}
			if (camX.Value + camTransStep.Value <= FloorPlan.MaxX) {
				camPosUR.Enabled = (camY.Value + camTransStep.Value) <= FloorPlan.MaxY; ;
				camPosR.Enabled = true;
				camPosDR.Enabled = (camY.Value - camTransStep.Value) >= 0; ;
			}
		}
		private void camPosUL_Click(object sender, EventArgs e) {
			camPosU_Click(sender, e);
			camPosL_Click(sender, e);
		}

		private void camX_ValueChanged(object sender, EventArgs e) {
			MethodInvoker processChange = delegate() {
				if (camX.Value - camTransStep.Value >= 0) {
					camPosUL.Enabled = (camY.Value + camTransStep.Value) <= FloorPlan.MaxY;
					camPosL.Enabled = true;
					camPosDL.Enabled = (camY.Value - camTransStep.Value) >= 0;
				}
				if (camX.Value + camTransStep.Value > FloorPlan.MaxX) {
					camPosUR.Enabled = false;
					camPosR.Enabled = false;
					camPosDR.Enabled = false;
				}
				if (camX.Value - camTransStep.Value < 0) {
					camPosUL.Enabled = false;
					camPosL.Enabled = false;
					camPosDL.Enabled = false;
				}
				if (camX.Value + camTransStep.Value <= FloorPlan.MaxX) {
					camPosUR.Enabled = (camY.Value + camTransStep.Value) <= FloorPlan.MaxY; ;
					camPosR.Enabled = true;
					camPosDR.Enabled = (camY.Value - camTransStep.Value) >= 0; ;
				}
			};
			Invoke(processChange);
			cam.X = (int)camX.Value;
		}
		private void camY_ValueChanged(object sender, EventArgs e) {
			MethodInvoker processChange = delegate() {
				if (camY.Value - camTransStep.Value >= 0) {
					camPosDR.Enabled = (camX.Value + camTransStep.Value) <= FloorPlan.MaxX;
					camPosD.Enabled = true;
					camPosDL.Enabled = (camX.Value - camTransStep.Value) >= 0;
				}
				if (camY.Value + camTransStep.Value > FloorPlan.MaxY) {
					camPosUR.Enabled = false;
					camPosU.Enabled = false;
					camPosUL.Enabled = false;
				}
				if (camY.Value - camTransStep.Value < 0) {
					camPosDR.Enabled = false;
					camPosD.Enabled = false;
					camPosDL.Enabled = false;
				}
				if (camY.Value + camTransStep.Value <= FloorPlan.MaxY) {
					camPosUR.Enabled = (camX.Value + camTransStep.Value) <= FloorPlan.MaxX;
					camPosU.Enabled = true;
					camPosUL.Enabled = (camX.Value - camTransStep.Value) >= 0;
				}
			};
			Invoke(processChange);
			cam.Y = (int)camY.Value;
		}
		private void camTransStep_ValueChanged(object sender, EventArgs e) {
			camX_ValueChanged(sender, e);
			camY_ValueChanged(sender, e);
		}
		#endregion

		// Adjust Orientation
		#region Orientation
		private void camOriU_Click(object sender, EventArgs e) {
			camOrientation.Value = 90;
		}
		private void camOriUR_Click(object sender, EventArgs e) {
			camOrientation.Value = 45;
		}
		private void camOriR_Click(object sender, EventArgs e) {
			camOrientation.Value = 0;
		}
		private void camOriDR_Click(object sender, EventArgs e) {
			camOrientation.Value = 315;
		}
		private void camOriD_Click(object sender, EventArgs e) {
			camOrientation.Value = 270;
		}
		private void camOriDL_Click(object sender, EventArgs e) {
			camOrientation.Value = 225;
		}
		private void camOriL_Click(object sender, EventArgs e) {
			camOrientation.Value = 180;
		}
		private void camOriUL_Click(object sender, EventArgs e) {
			camOrientation.Value = 135;
		}

		private void camOrientation_ValueChanged(object sender, EventArgs e) {
			if (camOrientation.Value < 0) camOrientation.Value = 359;
			if (camOrientation.Value > 359) camOrientation.Value = 0;
			cam.Orientation = (float)camOrientation.Value;
		}
		#endregion

		// Adjusr Pitch
		#region Pitch
		private void camPitchBar_Scroll(object sender, EventArgs e) {
			camPitch.Value = camPitchBar.Value;
		}

		private void camPitch_ValueChanged(object sender, EventArgs e) {
			camPitchBar.Value = (int)camPitch.Value;
			cam.Pitch = Convert.ToDouble(camPitch.Value);
		}
		#endregion

		/// <summary>
		/// Event that is triggered when the merger of the cam should be changed.
		/// </summary>
		/// <param name="sender">Not used.</param>
		/// <param name="e">Not used.</param>
		private void mergerSelect_SelectedIndexChanged(object sender, EventArgs e) {
			// find target merger
			SensorMerger merger = Program.Mergers.Find(new Predicate<SensorMerger>(delegate(SensorMerger current) {
				return current.Name == mergerSelect.Text;
			}));

			if (merger != null) {
				// change to local merger
				cam.Merger = merger;
			} else {
				// did not find a local merger, try remote merger
				String[] mergerData = mergerSelect.Text.Split('@');
				String mergerName = mergerData[0];
				byte mergerNetworkID = (byte)int.Parse(mergerData[1]);
				merger = Program.RemoteMergers.Find(new Predicate<SensorMerger>(delegate(SensorMerger current) {
					return current.Name.Equals(mergerData[0]) && ((RemoteMerger)current).NetworkID == mergerNetworkID;
				}));
				if (merger != null) {
					// change to remote merger
					cam.Merger = merger;
				}
			}
		}

		/// <summary>
		/// Event that is triggered when a new merger should be added.
		/// The cam merger should also be set to the new merger.
		/// </summary>
		/// <param name="sender">Not used.</param>
		/// <param name="e">Not used.</param>
		private void mergerAdd_Click(object sender, EventArgs e) {
			// test if merger already exists
			SensorMerger merger = Program.Mergers.Find(new Predicate<SensorMerger>(delegate(SensorMerger current) {
				return current.Name == mergerSelect.Text
					|| current.Name == mergerSelect.Text.Split('@')[0];
			}));
			bool isMergerNew = (merger == null && !mergerSelect.Text.Contains("@"));

			if (isMergerNew) {
				// create new merger
				SensorMerger newMerger = new LocalMerger(mergerSelect.Text);
				cam.Merger = newMerger;
				Program.Mergers.Add(newMerger);
				// notify network
				NetworkConnector.SendMergerChanged(newMerger.Name);
				// update GUI
				Program.RefreshMergerList();
				mergerSelect.Items.Add(newMerger.Name);
				Program.Log("Added new merger: " + newMerger.Name);
			} else if (merger != null) {
				// merger already exists, just change it
				cam.Merger = merger;
			} else {
				// merger could not be created
				mergerSelect.Text = cam.Merger.Name;
			}
		}

		/// <summary>
		/// Event that is triggered if the name of the cam should be changed.
		/// </summary>
		/// <param name="sender">Not used.</param>
		/// <param name="e">Not used.</param>
		private void nameBox_TextChanged(object sender, EventArgs e) {
			Text = "Properties of " + nameBox.Text;
			if (cam.Name != nameBox.Text)
				cam.Name = nameBox.Text;
		}

		/// <summary>
		/// Event that is triggered when the window is closing.
		/// </summary>
		/// <param name="sender">Not used.</param>
		/// <param name="e">Not used.</param>
		private void CamPropertiesWindow_FormClosing(object sender, FormClosingEventArgs e) {
			Program.UpdateCoverageMap(cam, true);
		}

	}
}
